Shadow DOM in Selenium
Shadow DOM is a web platform technology used to encapsulate the internal HTML structure, CSS, and behavior of a web component. Elements inside a Shadow DOM are separated from the normal document DOM by a shadow boundary. This encapsulation can make normal Selenium locators such as XPath or CSS selectors from the main document unable to directly locate elements inside the shadow tree.
In Selenium automation, understanding Shadow DOM is important because modern web applications frequently use Web Components and custom elements. Selenium provides support for accessing Shadow DOM through the ShadowRoot interface and the getShadowRoot() method.
Shadow DOM consists of important concepts such as the Shadow Host, Shadow Root, Shadow Tree, and Shadow Boundary. Once the shadow root is accessed, Selenium can locate elements inside it using appropriate locators.
Course Resource: Selenium Training | Register for Course Demo
1. What is Shadow DOM?
Shadow DOM is a DOM tree attached to an element that provides encapsulation for the internal structure of a web component. Instead of placing all elements directly inside the normal document DOM, a component can maintain its internal elements inside a separate shadow tree.
For example, a custom web component may look like this from the outside:
<my-login></my-login>
Internally, the component may contain elements such as:
#shadow-root
<input id="username">
<input id="password">
<button>Login</button>
The input and button are inside the shadow tree rather than directly inside the normal DOM tree.
2. Why is Shadow DOM Important in Selenium?
Modern web applications increasingly use Web Components and custom elements. When Selenium tests interact with such applications, normal locators may not be sufficient if the target element is located inside a shadow tree.
- Allows testing of modern Web Components.
- Helps automate elements hidden behind Shadow DOM boundaries.
- Supports automation of custom HTML elements.
- Allows Selenium to interact with elements inside open Shadow DOM.
- Helps testers understand why normal XPath or CSS selectors may fail.
- Supports reusable component-based web applications.
- Can be combined with Page Object Model.
- Can be used with explicit waits and assertions.
3. Shadow DOM Terminology
| Term | Description |
| Shadow Host | The normal DOM element to which the Shadow DOM is attached. |
| Shadow Root | The root object representing the Shadow DOM tree. |
| Shadow Tree | The DOM structure contained inside the Shadow Root. |
| Shadow Boundary | The boundary separating the normal document DOM from the shadow tree. |
| Web Component | A reusable custom HTML component that can use Shadow DOM for encapsulation. |
| Custom Element | A developer-defined HTML element such as <my-login>. |
4. Shadow DOM Structure
A typical Shadow DOM structure can be represented as follows:
HTML Document
|
+-- Normal DOM
|
+-- Shadow Host
|
+-- Shadow Root
|
+-- Shadow Tree
|
+-- Input
+-- Button
+-- Label
Selenium first locates the Shadow Host, obtains its Shadow Root, and then locates the required element inside that root.
5. Normal DOM vs Shadow DOM
| Normal DOM | Shadow DOM |
| Elements are directly part of the document tree. | Elements belong to a shadow tree. |
| Normal Selenium locators generally work directly. | The shadow root may need to be accessed first. |
| Elements are directly reachable from the document. | Elements are separated by a shadow boundary. |
| CSS styles can affect normal document elements. | Shadow DOM provides style encapsulation. |
| Common in traditional web pages. | Common in Web Components and custom elements. |
6. What is a Shadow Host?
A Shadow Host is the normal DOM element to which a Shadow DOM is attached.
For example:
<my-login id="loginComponent"></my-login>
Here, my-login is the Shadow Host if a Shadow Root is attached to it.
The host can normally be located from the main document using Selenium.
WebElement host = driver.findElement(
By.id("loginComponent")
);
7. What is a Shadow Root?
The Shadow Root is the root of the Shadow DOM tree. Selenium provides the ShadowRoot interface to interact with elements inside an accessible shadow tree.
In Selenium 4, the Shadow Root can be obtained using:
ShadowRoot shadowRoot = host.getShadowRoot();
After obtaining the shadow root, elements inside it can be located.
8. What is a Shadow Tree?
A Shadow Tree is the collection of elements contained inside the Shadow Root.
For example:
<my-login>
#shadow-root
<div>
<input id="username">
<input id="password">
<button id="login">Login</button>
</div>
</my-login>
The input fields and button belong to the shadow tree.
9. What is a Shadow Boundary?
The Shadow Boundary separates the regular document DOM from the Shadow DOM.
This boundary is important for Selenium because a selector used against the main document does not automatically traverse into every shadow tree.
Document DOM
|
+-- <my-login> <-- Shadow Host
|
| Shadow Boundary
|
+-- #shadow-root
|
+-- <input>
+-- <button>
10. Open Shadow DOM
An open Shadow DOM exposes its shadow root through the host's shadowRoot property in browser JavaScript. Selenium can access an open shadow root through supported WebDriver APIs such as getShadowRoot().
Example web component:
const host = document.querySelector("#host");
const shadow = host.attachShadow({ mode: "open" });
Because the shadow root is open, automation code can access the shadow tree through supported browser automation mechanisms.
11. Closed Shadow DOM
A closed Shadow DOM does not expose its shadow root through the normal shadowRoot property.
const shadow = host.attachShadow({ mode: "closed" });
For a closed shadow root, direct access from normal page JavaScript is restricted. Selenium automation should therefore not assume that every shadow tree can be accessed in the same way.
When designing automation for a closed Shadow DOM, the tester should consider the application's supported interfaces, accessible user-facing behavior, and the framework's capabilities rather than relying on unsupported DOM manipulation.
12. Open vs Closed Shadow DOM
| Feature | Open Shadow DOM | Closed Shadow DOM |
| Shadow root exposed through host.shadowRoot | Yes | No |
| Automation accessibility | Generally easier | More restricted |
| Encapsulation | Yes | Stronger external encapsulation |
| Selenium getShadowRoot() | Can be used when supported | Direct access may not be available |
13. Selenium 4 and Shadow DOM
Selenium 4 provides native WebDriver support for working with accessible Shadow DOM through the ShadowRoot interface. This reduces the need for custom JavaScript-based workarounds for common open Shadow DOM scenarios.
A typical Selenium 4 approach is:
WebElement host = driver.findElement(
By.cssSelector("my-component")
);
ShadowRoot shadowRoot = host.getShadowRoot();
WebElement element = shadowRoot.findElement(
By.cssSelector("#username")
);
element.sendKeys("admin");
14. Basic Selenium Shadow DOM Example
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.SearchContext;
public class ShadowDomExample {
public static void main(String[] args) {
WebDriver driver = new ChromeDriver();
driver.get("https://example.com");
WebElement host = driver.findElement(
By.cssSelector("my-component")
);
SearchContext shadowRoot = host.getShadowRoot();
WebElement input = shadowRoot.findElement(
By.cssSelector("#username")
);
input.sendKeys("admin");
driver.quit();
}
}
15. Finding the Shadow Host
The first step is to locate the Shadow Host from the normal document DOM.
WebElement host = driver.findElement(
By.cssSelector("my-component")
);
Other Selenium locators can also be used when applicable:
WebElement host = driver.findElement(
By.id("component")
);
WebElement host = driver.findElement(
By.cssSelector("my-login")
);
WebElement host = driver.findElement(
By.xpath("//my-login")
);
16. Getting the Shadow Root
After locating the host, use getShadowRoot() to obtain the shadow root.
WebElement host = driver.findElement(
By.cssSelector("my-login")
);
ShadowRoot shadowRoot = host.getShadowRoot();
The returned ShadowRoot acts as a search context for locating elements inside the shadow tree.
17. Locating an Element Inside Shadow DOM
Once the shadow root is available, use findElement() on the shadow root.
ShadowRoot shadowRoot = host.getShadowRoot();
WebElement username = shadowRoot.findElement(
By.cssSelector("#username")
);
username.sendKeys("admin");
The important point is that the search is performed against the shadow root rather than directly against the WebDriver document context.
18. Shadow DOM Login Example
WebElement loginHost = driver.findElement(
By.cssSelector("login-component")
);
ShadowRoot shadowRoot = loginHost.getShadowRoot();
WebElement username = shadowRoot.findElement(
By.id("username")
);
WebElement password = shadowRoot.findElement(
By.id("password")
);
WebElement loginButton = shadowRoot.findElement(
By.id("loginButton")
);
username.sendKeys("admin");
password.sendKeys("admin123");
loginButton.click();
19. Why Normal Locators May Fail
Suppose the application contains:
<login-component>
#shadow-root
<input id="username">
</login-component>
The following direct search may not find the input because the input belongs to the shadow tree:
driver.findElement(
By.id("username")
);
The correct approach is to first locate the host, obtain the shadow root, and then search inside it.
WebElement host = driver.findElement(
By.cssSelector("login-component")
);
ShadowRoot shadowRoot = host.getShadowRoot();
WebElement username = shadowRoot.findElement(
By.id("username")
);
20. Shadow DOM Flow in Selenium
Start Test
|
v
Load Web Page
|
v
Locate Shadow Host
|
v
Get Shadow Root
|
v
Search Inside Shadow Root
|
v
Locate Target Element
|
v
Perform Action
|
v
Validate Result
|
v
End Test
21. Shadow DOM with CSS Selectors
CSS selectors are commonly used to locate elements inside a shadow root.
ShadowRoot shadowRoot = host.getShadowRoot();
WebElement button = shadowRoot.findElement(
By.cssSelector("button.login")
);
button.click();
CSS selectors can be particularly convenient when working with custom elements and component-specific attributes.
22. Shadow DOM with ID Locator
WebElement username = shadowRoot.findElement(
By.id("username")
);
username.sendKeys("admin");
The ID belongs to the element inside the shadow tree, not necessarily to the Shadow Host.
23. Shadow DOM with XPath
XPath can be used by Selenium within an appropriate search context where supported.
WebElement button = shadowRoot.findElement(
By.xpath(".//button[@id='login']")
);
button.click();
However, the important concept is not the locator type itself. The critical step is searching from the correct ShadowRoot context.
24. Nested Shadow DOM
Modern component-based applications can contain multiple nested Shadow DOM layers.
For example:
<outer-component>
#shadow-root
<inner-component>
#shadow-root
<button id="submit">
</inner-component>
</outer-component>
In such cases, Selenium must traverse each accessible shadow root.
25. Selenium Code for Nested Shadow DOM
WebElement outerHost = driver.findElement(
By.cssSelector("outer-component")
);
ShadowRoot outerShadow = outerHost.getShadowRoot();
WebElement innerHost = outerShadow.findElement(
By.cssSelector("inner-component")
);
ShadowRoot innerShadow = innerHost.getShadowRoot();
WebElement button = innerShadow.findElement(
By.cssSelector("#submit")
);
button.click();
26. Nested Shadow DOM Flow
WebDriver
|
v
Outer Shadow Host
|
v
Outer Shadow Root
|
v
Inner Shadow Host
|
v
Inner Shadow Root
|
v
Target Element
|
v
Action
27. Multiple Levels of Shadow DOM
Some applications can contain several nested components. The general approach remains the same:
- Locate the first shadow host.
- Get its shadow root.
- Locate the next shadow host inside that root.
- Get the next shadow root.
- Repeat until the target element is reached.
- Perform the required Selenium action.
28. Shadow DOM with Web Components
Shadow DOM is closely associated with Web Components. Web Components are a set of web platform technologies that can include custom elements, Shadow DOM, and HTML templates.
For example:
<user-profile></user-profile>
The component may internally contain:
#shadow-root
<div class="profile">
<span class="name">John</span>
<button class="edit">Edit</button>
</div>
Selenium can interact with accessible component internals by obtaining the appropriate shadow root.
29. Shadow DOM and Custom Elements
A custom element is an HTML element defined by the application developer.
<user-card></user-card>
<login-form></login-form>
<product-card></product-card>
Custom elements often use Shadow DOM to encapsulate their internal implementation. This is why Selenium testers should be familiar with both custom elements and Shadow DOM.
30. Shadow DOM and Selenium Page Object Model
Shadow DOM can be integrated into the Page Object Model. The Page Object can encapsulate the process of locating the Shadow Host, retrieving the Shadow Root, and interacting with internal elements.
public class LoginComponent {
private WebDriver driver;
public LoginComponent(WebDriver driver) {
this.driver = driver;
}
public void login(String username, String password) {
WebElement host = driver.findElement(
By.cssSelector("login-component")
);
ShadowRoot shadowRoot = host.getShadowRoot();
shadowRoot.findElement(
By.id("username")
).sendKeys(username);
shadowRoot.findElement(
By.id("password")
).sendKeys(password);
shadowRoot.findElement(
By.id("loginButton")
).click();
}
}
31. Shadow DOM Page Object Benefits
- Hides Shadow DOM implementation details from test methods.
- Improves test readability.
- Reduces duplicate shadow-root traversal code.
- Makes component interaction reusable.
- Centralizes locator maintenance.
- Works well with large Selenium frameworks.
- Supports component-based application testing.
32. Shadow DOM with Explicit Waits
Shadow DOM elements may be dynamically created after the page loads. In such situations, synchronization is important.
A practical approach is to wait for the Shadow Host to become available before retrieving its shadow root.
WebDriverWait wait = new WebDriverWait(
driver,
Duration.ofSeconds(10)
);
WebElement host = wait.until(
ExpectedConditions.presenceOfElementLocated(
By.cssSelector("login-component")
)
);
ShadowRoot shadowRoot = host.getShadowRoot();
After obtaining the shadow root, the target element can be located and additional waits can be applied according to the application's behavior.
33. Shadow DOM and Dynamic Elements
Some Web Components are rendered dynamically. The Shadow Host may exist before its internal elements are fully created.
A robust test should consider:
- Host availability.
- Shadow root availability.
- Internal element rendering.
- Element visibility.
- Element interactability.
- Application loading behavior.
34. Shadow DOM with JavaScript
JavaScript can be used to inspect or interact with web components when necessary. However, Selenium 4 provides native Shadow DOM support for common accessible cases, so JavaScript should not automatically be the first choice.
For example, browser JavaScript can access an open shadow root:
WebElement shadowRoot = (WebElement) ((JavascriptExecutor) driver)
.executeScript(
"return arguments[0].shadowRoot",
host
);
The exact return handling depends on the Selenium version and WebDriver implementation. Native Selenium ShadowRoot support is generally preferable when it meets the test requirement.
35. Native Selenium vs JavaScript Approach
| Native Selenium ShadowRoot | JavaScript Approach |
| Uses Selenium WebDriver APIs. | Executes JavaScript in the browser. |
| Cleaner automation code for supported scenarios. | Useful for specialized browser-side operations. |
| Works naturally with Selenium search contexts. | Requires JavaScript knowledge. |
| Preferred for normal Shadow DOM interaction when supported. | Useful as a fallback for special cases. |
36. Shadow DOM and Browser Developer Tools
Chrome, Edge, and other browser developer tools can help testers understand Shadow DOM structures.
When inspecting a Web Component, the Elements panel may show a structure similar to:
<login-component>
#shadow-root (open)
<div class="login">
<input id="username">
<input id="password">
<button id="login">Login</button>
</div>
</login-component>
This structure helps the tester identify the Shadow Host and understand how many shadow boundaries must be crossed.
37. How to Identify Shadow DOM in DevTools
- Open the application in the browser.
- Open Developer Tools.
- Select the Elements or Inspector panel.
- Locate the custom component.
- Look for a #shadow-root entry.
- Identify whether the shadow root is open or otherwise accessible.
- Inspect the elements inside the shadow tree.
- Build Selenium locators based on the component structure.
38. Shadow DOM Example with Search Component
WebElement searchHost = driver.findElement(
By.cssSelector("search-component")
);
ShadowRoot shadowRoot = searchHost.getShadowRoot();
WebElement searchBox = shadowRoot.findElement(
By.cssSelector("input.search")
);
WebElement searchButton = shadowRoot.findElement(
By.cssSelector("button.search")
);
searchBox.sendKeys("Selenium");
searchButton.click();
39. Shadow DOM Example with Product Component
WebElement productHost = driver.findElement(
By.cssSelector("product-card")
);
ShadowRoot shadowRoot = productHost.getShadowRoot();
String productName = shadowRoot.findElement(
By.cssSelector(".product-name")
).getText();
WebElement addButton = shadowRoot.findElement(
By.cssSelector(".add-to-cart")
);
addButton.click();
System.out.println(productName);
40. Shadow DOM Example with Form Component
WebElement formHost = driver.findElement(
By.cssSelector("registration-form")
);
ShadowRoot shadowRoot = formHost.getShadowRoot();
shadowRoot.findElement(
By.id("name")
).sendKeys("John");
shadowRoot.findElement(
By.id("email")
).sendKeys("[email protected]");
shadowRoot.findElement(
By.id("mobile")
).sendKeys("9876543210");
shadowRoot.findElement(
By.id("submit")
).click();
41. Shadow DOM and Assertions
After interacting with an element inside a Shadow DOM, TestNG assertions can be used to verify the result.
WebElement host = driver.findElement(
By.cssSelector("login-component")
);
ShadowRoot shadowRoot = host.getShadowRoot();
String message = shadowRoot.findElement(
By.cssSelector(".message")
).getText();
Assert.assertEquals(
message,
"Login successful"
);
42. Shadow DOM with TestNG
Shadow DOM testing can be integrated directly into TestNG test classes.
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.SearchContext;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class ShadowDomTest {
WebDriver driver;
@BeforeMethod
public void setup() {
driver = new ChromeDriver();
driver.manage().window().maximize();
driver.get("https://example.com");
}
@Test
public void shadowDomTest() {
WebElement host = driver.findElement(
By.cssSelector("login-component")
);
SearchContext shadowRoot = host.getShadowRoot();
WebElement username = shadowRoot.findElement(
By.id("username")
);
username.sendKeys("admin");
Assert.assertTrue(username.isDisplayed());
}
@AfterMethod
public void tearDown() {
if (driver != null) {
driver.quit();
}
}
}
43. Shadow DOM and Data-Driven Testing
Shadow DOM components can also be tested using TestNG Data Providers. The component interaction remains the same while the input data changes.
@DataProvider(name = "loginData")
public Object[][] loginData() {
return new Object[][] {
{"admin", "admin123"},
{"manager", "manager123"},
{"user", "user123"}
};
}
@Test(dataProvider = "loginData")
public void shadowLoginTest(
String username,
String password) {
WebElement host = driver.findElement(
By.cssSelector("login-component")
);
ShadowRoot shadowRoot = host.getShadowRoot();
shadowRoot.findElement(
By.id("username")
).sendKeys(username);
shadowRoot.findElement(
By.id("password")
).sendKeys(password);
}
44. Shadow DOM and Multiple Components
A page can contain multiple independent Shadow DOM components.
Page
|
+-- Header Component
| |
| +-- Shadow Root
|
+-- Search Component
| |
| +-- Shadow Root
|
+-- Product Component
| |
| +-- Shadow Root
|
+-- Checkout Component
|
+-- Shadow Root
Each component can be treated as an independent automation component and represented by a dedicated Page Object or component object.
45. Component-Based Page Object Design
For applications containing many Web Components, a component-oriented automation architecture can be useful.
Base Test
|
+-- HeaderComponent
|
+-- SearchComponent
|
+-- LoginComponent
|
+-- ProductComponent
|
+-- CheckoutComponent
|
+-- Selenium WebDriver
Each component class can encapsulate its own Shadow Host locator, Shadow Root traversal, internal locators, and actions.
46. Reusable Shadow Root Utility
A reusable utility method can reduce duplicate code when many tests need to retrieve Shadow Roots.
public class ShadowDomUtils {
public static ShadowRoot getShadowRoot(
WebDriver driver,
By hostLocator) {
WebElement host = driver.findElement(hostLocator);
return host.getShadowRoot();
}
}
The utility can then be used by test or component classes.
ShadowRoot shadowRoot =
ShadowDomUtils.getShadowRoot(
driver,
By.cssSelector("login-component")
);
47. Reusable Nested Shadow DOM Utility
For applications with nested components, a helper method can traverse multiple shadow hosts.
public static SearchContext getNestedShadowRoot(
WebDriver driver,
By outerHostLocator,
By innerHostLocator) {
WebElement outerHost =
driver.findElement(outerHostLocator);
SearchContext outerShadow =
outerHost.getShadowRoot();
WebElement innerHost =
outerShadow.findElement(innerHostLocator);
return innerHost.getShadowRoot();
}
48. Shadow DOM and SearchContext
Selenium's SearchContext abstraction is useful when working with Shadow DOM because both WebDriver and ShadowRoot can be used as contexts for locating elements.
SearchContext shadowRoot = host.getShadowRoot();
WebElement element = shadowRoot.findElement(
By.cssSelector("#username")
);
This allows framework code to work with a search context without unnecessarily coupling every method to a specific implementation.
49. Shadow DOM with Relative Locators
When working inside a shadow root, the primary requirement remains obtaining the correct search context. Once the appropriate Selenium search context is available, suitable Selenium locator strategies can be applied according to the element structure and framework support.
For most Shadow DOM automation scenarios, clear CSS selectors or stable IDs are usually easier to maintain than complex locator expressions.
50. Shadow DOM and Dynamic Web Components
Web Components may be created after JavaScript execution, API calls, or user interactions. Therefore, a test should not assume that the Shadow Host and its internal elements are immediately available.
A robust automation framework should:
- Wait for the component to appear.
- Obtain the shadow root after the component is ready.
- Locate internal elements after rendering.
- Wait for required elements to become interactable.
- Avoid unnecessary hard waits.
51. Common Shadow DOM Exceptions
Shadow DOM automation can result in familiar Selenium exceptions when the application structure or synchronization is incorrect.
| Exception | Possible Cause |
| NoSuchElementException | Incorrect host or internal element locator. |
| TimeoutException | Component or element did not become available within the wait duration. |
| StaleElementReferenceException | Component was re-rendered and a previous element reference became invalid. |
| ElementNotInteractableException | Element exists but is not currently interactable. |
| JavascriptException | Invalid or unsupported JavaScript operation. |
52. Common Mistake: Searching Directly from Driver
One of the most common mistakes is trying to locate a shadow element directly through WebDriver.
Incorrect approach:
driver.findElement(
By.id("shadowElement")
);
Correct approach:
WebElement host = driver.findElement(
By.cssSelector("my-component")
);
ShadowRoot shadowRoot = host.getShadowRoot();
WebElement element = shadowRoot.findElement(
By.id("shadowElement")
);
53. Common Mistake: Using the Wrong Shadow Host
If the wrong host is selected, Selenium may retrieve a valid Shadow Root that does not contain the target element.
Always inspect the DOM and identify the exact component containing the target element.
54. Common Mistake: Ignoring Nested Shadow Roots
If the target element is several Shadow DOM levels deep, accessing only the first shadow root is not enough.
Outer Host
|
Outer Shadow Root
|
Inner Host
|
Inner Shadow Root
|
Target
Each accessible boundary must be traversed.
55. Common Mistake: Hard-Coded Sleeps
Using large Thread.sleep() calls is generally not a reliable synchronization strategy.
Instead of:
Thread.sleep(5000);
Prefer appropriate explicit waits and application-aware synchronization.
56. Common Mistake: Assuming Every Shadow Root is Open
Automation code should not assume that every component exposes its Shadow Root in the same way. The accessibility of the shadow tree depends on how the component is implemented.
57. Shadow DOM Best Practices
- Understand the component structure before writing locators.
- Identify the Shadow Host first.
- Use Selenium's native ShadowRoot support when appropriate.
- Prefer stable IDs and CSS selectors.
- Keep Shadow DOM traversal inside Page Objects or component classes.
- Use explicit waits for dynamic components.
- Handle nested Shadow DOM carefully.
- Avoid unnecessary JavaScript when native Selenium support is sufficient.
- Do not share WebDriver instances unsafely during parallel execution.
- Keep Shadow DOM utilities reusable.
- Use assertions to validate user-visible behavior.
- Keep locators centralized and maintainable.
58. Shadow DOM with Parallel Execution
Shadow DOM tests can be executed in parallel, but the entire automation framework must be thread-safe.
Thread 1
|
+-- WebDriver 1
|
+-- Shadow Component
Thread 2
|
+-- WebDriver 2
|
+-- Shadow Component
Each concurrent test should generally have an isolated WebDriver session and isolated test state.
59. Shadow DOM and Reporting
Shadow DOM tests can be integrated with TestNG or other reporting frameworks. A useful report should identify the component being tested, the action performed, and the result.
Shadow DOM Test
|
+-- Locate Host PASS
|
+-- Get Shadow Root PASS
|
+-- Locate Element PASS
|
+-- Perform Action PASS
|
+-- Assertion PASS
For failed tests, logs should provide enough information to identify which component and action caused the failure.
60. Shadow DOM Practical Project Structure
src
|-- test
|-- java
|-- tests
| |-- LoginTest.java
| |-- SearchTest.java
| |-- CheckoutTest.java
|
|-- components
| |-- LoginComponent.java
| |-- SearchComponent.java
| |-- ProductComponent.java
| |-- CheckoutComponent.java
|
|-- pages
| |-- HomePage.java
| |-- LoginPage.java
|
|-- utilities
|-- ShadowDomUtils.java
|-- DriverFactory.java
|-- ConfigReader.java
61. Complete Practical Shadow DOM Example
The following example demonstrates a basic Selenium and TestNG structure for interacting with an open Shadow DOM component.
import java.time.Duration;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class ShadowDomTest {
private WebDriver driver;
private WebDriverWait wait;
@BeforeMethod
public void setup() {
driver = new ChromeDriver();
driver.manage().window().maximize();
wait = new WebDriverWait(
driver,
Duration.ofSeconds(10)
);
driver.get("https://example.com");
}
@Test
public void loginUsingShadowDom() {
WebElement host = wait.until(
ExpectedConditions.presenceOfElementLocated(
By.cssSelector("login-component")
)
);
SearchContext shadowRoot =
host.getShadowRoot();
WebElement username =
shadowRoot.findElement(
By.id("username")
);
WebElement password =
shadowRoot.findElement(
By.id("password")
);
WebElement loginButton =
shadowRoot.findElement(
By.id("loginButton")
);
username.sendKeys("admin");
password.sendKeys("admin123");
loginButton.click();
Assert.assertTrue(
driver.getTitle().contains("Dashboard")
);
}
@AfterMethod
public void tearDown() {
if (driver != null) {
driver.quit();
}
}
}
62. Real-World Shadow DOM Architecture
TestNG Test
|
v
Page Object / Component Object
|
v
Shadow DOM Utility
|
v
Locate Shadow Host
|
v
Get Shadow Root
|
v
Locate Internal Element
|
v
Selenium Action
|
v
Application
|
v
Assertion
|
v
Test Report
63. Shadow DOM vs iframe
Shadow DOM and iframes are different concepts. Both can make automation require an additional context-handling step, but the mechanisms are not interchangeable.
| Shadow DOM | iframe |
| Provides DOM and style encapsulation. | Embeds another browsing context/document. |
| Uses ShadowRoot as a search context. | Uses frame switching. |
| Accessed through a Shadow Host. | Accessed through a frame element. |
| Selenium 4 provides ShadowRoot support. | Selenium provides switchTo().frame(). |
| Common with Web Components. | Common for embedded documents or applications. |
64. Shadow DOM vs Normal DOM vs iframe
| Feature | Normal DOM | Shadow DOM | iframe |
| Structure | Main document tree | Encapsulated tree | Separate document context |
| Selenium Context | WebDriver | ShadowRoot/SearchContext | Frame context |
| Typical Component | Standard HTML | Web Component | Embedded page |
| Primary Concept | DOM | Encapsulation | Browsing context |
65. Shadow DOM Learning Roadmap
- Understand the DOM.
- Learn Web Components.
- Understand Shadow Host and Shadow Root.
- Understand Shadow Tree and Shadow Boundary.
- Learn open and closed Shadow DOM.
- Inspect Shadow DOM using browser Developer Tools.
- Learn Selenium 4 ShadowRoot support.
- Practice locating Shadow Hosts.
- Practice locating elements inside Shadow Roots.
- Practice nested Shadow DOM.
- Combine Shadow DOM with explicit waits.
- Implement Shadow DOM Page Objects.
- Create reusable Shadow DOM utilities.
- Combine Shadow DOM with TestNG.
- Integrate Shadow DOM automation into a complete Selenium framework.
66. Practical Exercises
- Identify a Shadow Host using browser Developer Tools.
- Locate an open Shadow Root using Selenium.
- Enter data into an input inside Shadow DOM.
- Click a button inside a Shadow Root.
- Create a login test using Shadow DOM.
- Create a search test using Shadow DOM.
- Create a product-card component test.
- Automate a form inside a Shadow DOM.
- Automate nested Shadow DOM components.
- Create a reusable Shadow DOM utility.
- Create a Page Object for a Shadow DOM component.
- Add TestNG assertions to Shadow DOM tests.
- Run Shadow DOM tests with a Data Provider.
- Integrate Shadow DOM tests with test reporting.
67. Interview Questions on Shadow DOM
1. What is Shadow DOM?
Shadow DOM is an encapsulated DOM tree attached to an element, commonly used by Web Components to isolate internal structure and styling.
2. What is a Shadow Host?
The Shadow Host is the regular DOM element to which the Shadow DOM is attached.
3. What is a Shadow Root?
The Shadow Root is the root of a Shadow DOM tree and provides a search context for accessible elements inside that tree.
4. What is a Shadow Boundary?
The Shadow Boundary separates the normal document DOM from the shadow tree.
5. Why can normal Selenium locators fail with Shadow DOM?
Because the target element may exist inside a separate shadow tree rather than directly in the main document DOM.
6. How do you access an open Shadow DOM in Selenium 4?
First locate the Shadow Host and then call getShadowRoot() to obtain the ShadowRoot.
7. What is the difference between open and closed Shadow DOM?
An open shadow root is exposed through the host's standard shadow-root access mechanism, while a closed shadow root does not expose the root through the normal host.shadowRoot property.
8. Can Shadow DOM be automated using Selenium?
Yes. Selenium supports automation of accessible Shadow DOM through its ShadowRoot and search-context APIs.
9. Can Shadow DOM be nested?
Yes. Components can contain other components with their own Shadow Roots.
10. How do you handle nested Shadow DOM?
Locate the outer host, get its Shadow Root, locate the inner host, get its Shadow Root, and continue until the target element is reached.
11. Can Shadow DOM be used with Page Object Model?
Yes. Shadow DOM traversal can be encapsulated inside Page Objects or component objects.
12. Should JavaScript always be used for Shadow DOM?
No. Selenium's native ShadowRoot support should generally be considered first for supported scenarios. JavaScript can be useful for specialized cases.
13. What is SearchContext in Selenium?
SearchContext is an abstraction used by Selenium objects that can search for elements, including WebDriver and ShadowRoot.
14. How do you debug Shadow DOM tests?
Inspect the page in Developer Tools, identify the Shadow Host and shadow tree, verify the root structure, and then validate the Selenium locator chain.
15. Can Shadow DOM tests use explicit waits?
Yes. Explicit waits can be used to wait for the Shadow Host and other dynamic application conditions.
16. Can Shadow DOM tests run in parallel?
Yes, provided the automation framework is thread-safe and concurrent tests have properly isolated WebDriver instances and test data.
17. What is a common Shadow DOM automation mistake?
Trying to locate an element inside a Shadow Root directly from WebDriver without first obtaining the appropriate Shadow Root search context.
18. Is Shadow DOM the same as an iframe?
No. Shadow DOM provides DOM and style encapsulation, whereas an iframe creates a separate browsing context containing another document.
19. What is a Web Component?
A Web Component is a reusable custom HTML component built using web platform technologies such as Custom Elements, Shadow DOM, templates, and slots.
20. Why is Shadow DOM knowledge important for Selenium testers?
It enables testers to understand and automate modern component-based web applications where important user-facing elements may be implemented inside Web Components.
68. Quick Reference Table
| Concept | Purpose |
| Shadow DOM | Encapsulates a component's internal DOM and styles. |
| Shadow Host | Element to which the Shadow DOM is attached. |
| Shadow Root | Root/search context of the shadow tree. |
| Shadow Tree | Elements contained inside the Shadow Root. |
| Shadow Boundary | Boundary between normal DOM and shadow DOM. |
| getShadowRoot() | Obtains the accessible Shadow Root from a host. |
| SearchContext | Context used to locate elements. |
| Nested Shadow DOM | Multiple shadow boundaries that must be traversed. |
| Web Component | Reusable custom component that can use Shadow DOM. |
| POM | Encapsulates Shadow DOM interaction inside reusable objects. |
69. Advantages of Understanding Shadow DOM
- Improves automation coverage for modern web applications.
- Helps testers work with Web Components.
- Reduces confusion when normal locators cannot find an element.
- Supports reusable component-based automation.
- Works with Selenium 4 native Shadow DOM APIs.
- Can be integrated with Page Object Model.
- Can be combined with TestNG, Data Providers, waits, assertions, and reporting.
- Helps create maintainable automation for component-driven applications.
70. Limitations and Challenges
- Closed Shadow DOM can restrict direct access to the internal tree.
- Nested Shadow DOM can make locator chains more complex.
- Dynamic Web Components require proper synchronization.
- Component re-rendering can invalidate previously stored element references.
- Incorrectly identifying the Shadow Host can lead to locator failures.
- JavaScript-heavy applications may require additional synchronization.
- Complex component structures can increase Page Object implementation effort.
71. Summary
Shadow DOM is an important web platform technology that provides encapsulation for Web Components. It creates a separate shadow tree attached to a Shadow Host and separated from the normal document DOM by a Shadow Boundary.
For Selenium automation, the most important process is to locate the Shadow Host, obtain its accessible Shadow Root, and then locate the required element within that Shadow Root. Selenium 4 provides native support through the ShadowRoot API and methods such as getShadowRoot().
Shadow DOM automation becomes especially important when testing modern applications built with Web Components. For complex applications, Shadow DOM interaction can be encapsulated inside Page Objects or reusable component classes.
A robust Selenium framework should combine Shadow DOM support with stable locators, explicit waits, Page Object Model, TestNG, assertions, reusable utilities, thread-safe WebDriver management, and appropriate reporting.
72. Course Resources
Learn more about Selenium automation and related testing concepts:
Final Takeaway: Shadow DOM is not simply another locator technique. It represents a separate DOM context created for component encapsulation. In Selenium automation, understanding Shadow Hosts, Shadow Roots, Shadow Trees, Shadow Boundaries, open and closed Shadow DOM, nested components, and Selenium's native ShadowRoot support is essential for testing modern Web Component-based applications.